/**
 *  Created on  : 21/05/2012
 *  Author      : Ing. Javier Silva Pérez - [javier]
 *  Description :
 *  	Implements a set of functions for X509 certificates
 *  
 */
package cinvestav.cryptography.pki.android.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import org.spongycastle.asn1.DERBMPString;
import org.spongycastle.asn1.DERNull;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x500.X500NameBuilder;
import org.spongycastle.asn1.x500.style.BCStrictStyle;
import org.spongycastle.asn1.x500.style.BCStyle;
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.spongycastle.openssl.PEMReader;
import org.spongycastle.openssl.PEMWriter;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.spongycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.bc.BcRSAContentSignerBuilder;
import org.spongycastle.util.encoders.Hex;

import cinvestav.cryptography.pki.android.cert.CertificateInformationKeys;
import cinvestav.cryptography.pki.android.exception.AndroidCryptoUtilsException;
import cinvestav.cryptography.pki.android.key.RSAKeyPair;
import cinvestav.cryptography.pki.android.key.RSAPrivateKey;
import cinvestav.cryptography.pki.android.key.RSAPublicKey;

/**
 * Implements a set of functions for X509 certificates
 * 
 * @author Ing. Javier Silva Pérez - [javier]
 * 
 */
/**
 * @author Ing. Javier Silva Pérez - [javier]
 * @since 25/05/2012
 * @version 1.0
 */
public class AndroidX509CertificateUtils implements
		IAndroidX509CertificateUtils {

	JcaX509ExtensionUtils jcaX509ExtensionUtils;

	public AndroidX509CertificateUtils() throws AndroidCryptoUtilsException {
		super();
		try {
			jcaX509ExtensionUtils = new JcaX509ExtensionUtils();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		}
	}

	/**
	 * Generates a x500NameBuilder using a hashMap that contains the necessary
	 * information,
	 * 
	 * @param certificateInformationMap
	 *            Map filled out with the certificate information using the
	 *            Field key (
	 *            {@link cinvestav.cryptography.pki.android.cert.CertificateInformationKeys}
	 *            Supported Keys) and the field value
	 * @see cinvestav.cryptography.pki.android.cert.CertificateInformationKeys
	 * @return
	 */
	private static X500NameBuilder generateX500NameBuilder(
			HashMap<String, String> certificateInformationMap) {
		X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
		BCStyle style = new BCStrictStyle();

		Iterator<Entry<String, String>> it = certificateInformationMap
				.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String> pair = it.next();
			try {
				// Try to look up the key Name and add the field to the builder
				builder.addRDN(style.attrNameToOID(pair.getKey()),
						pair.getValue());
			} catch (IllegalArgumentException ex) {
				// If the key hasn't been found in the lookup table
			}
		}

		return builder;
	}

	/**
	 * Create X509 v3 Certificate for RSA Keys
	 * 
	 * @param pubKey
	 *            Public Key to be certified
	 * @param caPrivKey
	 *            PrivateKey of the signer CA
	 * @param serial
	 *            Serial Number for the certificate
	 * @param notBefore
	 *            Not Before Validity date
	 * @param notAfter
	 *            Not After Validity date
	 * @param caCert
	 *            CA PublicKey Certificate
	 * @param certificateInformationMap
	 *            Map filled out with the certificate information using the
	 *            Field key (
	 *            {@link cinvestav.cryptography.pki.android.cert.CertificateInformationKeys}
	 *            Supported Keys) and the field value
	 * @see cinvestav.cryptography.pki.android.cert.CertificateInformationKeys
	 * @return A new signed X509 Public key certificate
	 * @throws AndroidCryptoUtilsException
	 *             If something goes wrong
	 */
	public Certificate createV3Cert(RSAPublicKey pubKey,
			RSAPrivateKey caPrivKey, BigInteger serial, Date notBefore,
			Date notAfter, X509Certificate caCert,
			HashMap<String, String> certificateInformationMap)
			throws AndroidCryptoUtilsException {

		X509v3CertificateBuilder v3CertGen;

		// Get x500 name Builder from the certificateInformationMap
		X500NameBuilder builder = generateX500NameBuilder(certificateInformationMap);

		// Signer Algorithm ID
		AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
				.find("SHA256WithRSAEncryption");

		// Digest Algorithm ID
		AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
				.find(sigAlgId);

		ContentSigner contentSigner;
		try {
			// Create content Signer Object from using the CAs privateKey
			contentSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
					.build(caPrivKey.parseToRSAPrivateCrtKeyParameters());

			// Get the Public key contained in the certificate
			SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(
					new AlgorithmIdentifier(
							PKCSObjectIdentifiers.rsaEncryption,
							DERNull.INSTANCE), pubKey.parseToRSAPublicKey());

			//
			// create the certificate - version 3
			//
			v3CertGen = new X509v3CertificateBuilder(new X500Name(caCert
					.getIssuerX500Principal().getName()), serial, notBefore,
					notAfter, builder.build(), pubInfo);

			//
			// extensions
			//
			/*
			 * v3CertGen.addExtension( Extensions.SubjectKeyIdentifier, false,
			 * new SubjectKeyIdentifierStructure(pubKey));
			 * 
			 * v3CertGen.addExtension( X509Extensions.AuthorityKeyIdentifier,
			 * false, new AuthorityKeyIdentifierStructure(caCert));
			 * 
			 * v3CertGen.addExtension( X509Extensions.BasicConstraints, true,
			 * new BasicConstraints(0));
			 */

			X509CertificateHolder certHolder = v3CertGen.build(contentSigner);

			X509Certificate cert = new JcaX509CertificateConverter()
					.setProvider(AndroidCryptoUtils.PROVIDER).getCertificate(
							certHolder);

			cert.checkValidity(new Date());

			cert.verify(caCert.getPublicKey());

			PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;

			//
			// this is actually optional - but if you want to have control
			// over setting the friendly name this is the way to do it...
			//
			if (certificateInformationMap
					.containsKey(CertificateInformationKeys.FRIENDLY_NAME)) {
				bagAttr.setBagAttribute(
						PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
						new DERBMPString(certificateInformationMap
								.get(CertificateInformationKeys.FRIENDLY_NAME)));
			}

			SubjectKeyIdentifier keyID = SubjectKeyIdentifier
					.getInstance(jcaX509ExtensionUtils
							.createSubjectKeyIdentifier(pubKey
									.parseToJCERSAPublicKey()));
			bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
					keyID.toASN1Primitive());

			return cert;
		} catch (OperatorCreationException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (NoSuchAlgorithmException e) {
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #createV3Cert(cinvestav.cryptography.pki.android.key.RSAPublicKey,
	 * cinvestav.cryptography.pki.android.key.RSAPrivateKey,
	 * java.math.BigInteger, java.util.Date, java.util.Date, java.util.HashMap)
	 */
	@Override
	public Certificate createV3Cert(RSAPublicKey pubKey, RSAPrivateKey privKey,
			BigInteger serial, Date notBefore, Date notAfter,
			HashMap<String, String> certificateInformationMap)
			throws AndroidCryptoUtilsException {
		// TODO Auto-generated method stub
		X509v3CertificateBuilder v3CertGen;

		// Get x500 name Builder from the certificateInformationMap
		X500NameBuilder builder = generateX500NameBuilder(certificateInformationMap);

		// Signer Algorithm ID
		AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
				.find("SHA256WithRSAEncryption");

		// Digest Algorithm ID
		AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
				.find(sigAlgId);

		ContentSigner contentSigner;
		try {
			// Create content Signer Object from using the CAs privateKey
			contentSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
					.build(privKey.parseToRSAPrivateCrtKeyParameters());

			// Get the Public key contained in the certificate
			SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(
					new AlgorithmIdentifier(
							PKCSObjectIdentifiers.rsaEncryption,
							DERNull.INSTANCE), pubKey.parseToRSAPublicKey());

			//
			// create the certificate - version 3
			//
			v3CertGen = new X509v3CertificateBuilder(builder.build(), serial,
					notBefore, notAfter, builder.build(), pubInfo);

			//
			// extensions
			//
			/*
			 * v3CertGen.addExtension( Extensions.SubjectKeyIdentifier, false,
			 * new SubjectKeyIdentifierStructure(pubKey));
			 * 
			 * v3CertGen.addExtension( X509Extensions.AuthorityKeyIdentifier,
			 * false, new AuthorityKeyIdentifierStructure(caCert));
			 * 
			 * v3CertGen.addExtension( X509Extensions.BasicConstraints, true,
			 * new BasicConstraints(0));
			 */

			X509CertificateHolder certHolder = v3CertGen.build(contentSigner);
			X509Certificate cert = new JcaX509CertificateConverter()
					.setProvider(AndroidCryptoUtils.PROVIDER).getCertificate(
							certHolder);

			cert.checkValidity(new Date());

			cert.verify(pubKey.parseToJCERSAPublicKey());

			PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;

			//
			// this is actually optional - but if you want to have control
			// over setting the friendly name this is the way to do it...
			//
			if (certificateInformationMap
					.containsKey(CertificateInformationKeys.FRIENDLY_NAME)) {
				bagAttr.setBagAttribute(
						PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
						new DERBMPString(certificateInformationMap
								.get(CertificateInformationKeys.FRIENDLY_NAME)));
			}

			SubjectKeyIdentifier keyID = SubjectKeyIdentifier
					.getInstance(jcaX509ExtensionUtils
							.createSubjectKeyIdentifier(pubKey
									.parseToJCERSAPublicKey()));
			bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
					keyID.toASN1Primitive());

			return cert;
		} catch (OperatorCreationException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Create X509 V3 Cert error: "
					+ e, e.getCause());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #savePKCS12(java.lang.String,
	 * cinvestav.cryptography.pki.android.key.RSAKeyPair, java.lang.String,
	 * java.lang.String, java.security.cert.Certificate[])
	 */
	public void savePKCS12(String fileName, RSAKeyPair subjectKeyPair,
			String keyStorePwd, String privKeyPwd, Certificate[] chain)
			throws AndroidCryptoUtilsException {

		if (chain.length < 1) {
			throw new AndroidCryptoUtilsException(
					"Save PKCS12 error: No public certificate related to the privateKey found");
		}
		try {
			//
			// add the friendly name for the private key
			//
			PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) subjectKeyPair
					.getPrivateKey().parseToJCERSAPrivateCrtKey();

			//
			// this is also optional - in the sense that if you leave this
			// out the keystore will add it automatically, note though that
			// for the browser to recognise which certificate the private key
			// is associated with you should at least use the pkcs_9_localKeyId
			// OID and set it to the same as you do for the private key's
			// corresponding certificate.
			//
			RSAPublicKey pubKey = new RSAPublicKey();
			pubKey.parseRSAPublicKey(chain[0].getPublicKey());

			PKCS12BagAttributeCarrier bagAttrCert = (PKCS12BagAttributeCarrier) chain[0];

			SubjectKeyIdentifier keyID = SubjectKeyIdentifier
					.getInstance(jcaX509ExtensionUtils
							.createSubjectKeyIdentifier(chain[0].getPublicKey()));
			String keyIDStr = new String(Hex.encode(keyID.getKeyIdentifier()));
			if (bagAttrCert
					.getBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName) != null) {
				bagAttr.setBagAttribute(
						PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
						bagAttrCert
								.getBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName));
			}
			bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
					keyID.toASN1Primitive());

			//
			// store the key and the certificate chain
			//

			KeyStore store;

			store = KeyStore.getInstance("PKCS12", AndroidCryptoUtils.PROVIDER);

			store.load(null, null);

			//
			// if you haven't set the friendly name and local key id above
			// the name below will be the name of the key
			//
			store.setKeyEntry(keyIDStr, subjectKeyPair.getPrivateKey()
					.parseToJCERSAPrivateCrtKey(), privKeyPwd.toCharArray(),
					chain);

			FileOutputStream fOut = new FileOutputStream(fileName);

			store.store(fOut, keyStorePwd.toCharArray());

			fOut.close();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new AndroidCryptoUtilsException("Save PKCS12 error: " + e,
					e.getCause());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #loadRSAKeyPairPKCS12(java.lang.String, java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public RSAKeyPair loadRSAKeyPairPKCS12(String fileName, String keyStorePwd,
			String privKeyPwd) throws AndroidCryptoUtilsException {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #loadCertificateChainPKCS12(java.lang.String, java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public Certificate[] loadCertificateChainPKCS12(String fileName,
			String keyStorePwd, String privKeyPwd)
			throws AndroidCryptoUtilsException {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #saveCertificate(java.lang.String, java.security.cert.Certificate,
	 * java.lang.String)
	 */
	@Override
	public void saveCertificate(String fileName, Certificate certificate,
			String encoding) throws AndroidCryptoUtilsException {
		// Choose encoding type
		if (encoding.equalsIgnoreCase(AndroidCryptoUtils.ENCODING_DER)) {
			saveCertificateDER(fileName, certificate);
			return;
		}
		if (encoding.equalsIgnoreCase(AndroidCryptoUtils.ENCODING_PEM)) {
			saveCertificatePEM(fileName, certificate);
			return;
		}

		// If the encoding is different from DER or PEM throws the exception
		throw new AndroidCryptoUtilsException(
				"Save Certificate error: Unsupported Encoding [" + encoding
						+ "]");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #saveCertificate(java.lang.String, java.security.cert.Certificate)
	 */
	@Override
	public void saveCertificate(String fileName, Certificate certificate)
			throws AndroidCryptoUtilsException {
		saveCertificate(fileName, certificate, "DER");
	}

	/**
	 * Saves a certificate in PEM format
	 * 
	 * @param fileName
	 *            File in which the certificate will be saved
	 * @param certificate
	 *            Certificate to save
	 * @throws AndroidCryptoUtilsException
	 *             If writing error appears
	 */
	private void saveCertificatePEM(String fileName, Certificate certificate)
			throws AndroidCryptoUtilsException {

		try {
			File file = new File(fileName);
			if (!file.exists()) {
				file.createNewFile();
			}

			PEMWriter pWrt = new PEMWriter(new FileWriter(file),
					AndroidCryptoUtils.PROVIDER);

			pWrt.writeObject(certificate);

			pWrt.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save Certificate [PEM]: "
					+ e, e.getCause());
		}

	}

	/**
	 * Saves a certificate in DEM format
	 * 
	 * @param fileName
	 *            File in which the certificate will be saved
	 * @param certificate
	 *            Certificate to save
	 * @throws AndroidCryptoUtilsException
	 *             If writing error appears
	 */
	private void saveCertificateDER(String fileName, Certificate certificate)
			throws AndroidCryptoUtilsException {
		try {
			// Create file from fileName
			File file = new File(fileName);
			if (!file.exists()) {
				file.createNewFile();
			}
			// As the certificate is already encoded using DER format, we
			// must only get bytes and write them into the file
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(certificate.getEncoded());
			fos.flush();
			fos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save Certificate [DER]: "
					+ e, e.getCause());

		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save Certificate [DER]: "
					+ e, e.getCause());

		} catch (CertificateEncodingException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Save Certificate [DER]: "
					+ e, e.getCause());
		}

	}

	/**
	 * Load a X509 certificate encoded using DER from file
	 * 
	 * @param fileName
	 *            Full path and name of the file containing the x509 certificate
	 * @return X509 certificate contained in the file
	 * @throws AndroidCryptoUtilsException
	 *             If the file does not contain a valid DER X509 certificate
	 */
	private Certificate loadCertificatePEM(String fileName)
			throws AndroidCryptoUtilsException {
		Reader reader;
		PEMReader pemReader;
		try {
			reader = new FileReader(fileName);

			pemReader = new PEMReader(reader);
			Certificate cert = (Certificate) pemReader.readObject();
			if (cert == null) {
				throw new AndroidCryptoUtilsException(
						"Load Certificate [PEM] error: File does not contains a PEM encoded certificate");
			}
			return cert;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Load Certificate: "
					+ e, e.getCause());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new AndroidCryptoUtilsException("Load Certificate: "
					+ e, e.getCause());
		}
	}

	/**
	 * Load a X509 certificate encoded using DER from file
	 * 
	 * @param fileName
	 *            Full path and name of the file containing the x509 certificate
	 * @return X509 certificate contained in the file
	 * @throws AndroidCryptoUtilsException
	 *             If the file does not contain a valid DER X509 certificate
	 */
	private Certificate loadCertificateDER(String fileName)
			throws AndroidCryptoUtilsException {
		ByteArrayInputStream bIn;
		byte[] certBytes;

		File keyFile = new File(fileName);

		if (keyFile.exists()) {
			certBytes = new byte[(int) keyFile.length()];

			try {
				new FileInputStream(keyFile).read(certBytes);

				bIn = new ByteArrayInputStream(certBytes);

				CertificateFactory fact = CertificateFactory.getInstance(
						"X.509", AndroidCryptoUtils.PROVIDER);

				Certificate cert = fact.generateCertificate(bIn);

				return cert;

			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				throw new AndroidCryptoUtilsException(
						"Load Certificate: " + e, e.getCause());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new AndroidCryptoUtilsException(
						"Load Certificate: " + e, e.getCause());
			} catch (CertificateException e) {
				// TODO Auto-generated catch block
				throw new AndroidCryptoUtilsException(
						"Load Certificate: " + e, e.getCause());
			} catch (NoSuchProviderException e) {
				// TODO Auto-generated catch block
				throw new AndroidCryptoUtilsException(
						"Load Certificate: " + e, e.getCause());
			}
		} else {
			throw new AndroidCryptoUtilsException(
					"Load Certificate: No such file [" + fileName + "]");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #loadCertificate(java.lang.String, java.lang.String)
	 */
	@Override
	public Certificate loadCertificate(String fileName, String encoding)
			throws AndroidCryptoUtilsException {
		// Choose encoding type
		if (encoding.equalsIgnoreCase(AndroidCryptoUtils.ENCODING_DER)) {
			return loadCertificateDER(fileName);
		}
		if (encoding.equalsIgnoreCase(AndroidCryptoUtils.ENCODING_PEM)) {
			return loadCertificatePEM(fileName);
		}

		// If the encoding is different from DER or PEM throws the exception
		throw new AndroidCryptoUtilsException(
				"Load Certificate error: Unsupported Encoding [" + encoding
						+ "]");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cinvestav.cryptography.pki.android.utils.IAndroidX509CertificateUtils
	 * #loadCertificate(java.lang.String)
	 */
	@Override
	public Certificate loadCertificate(String fileName)
			throws AndroidCryptoUtilsException {
		Certificate c;
		try{
			c = loadCertificatePEM(fileName);
		}catch(AndroidCryptoUtilsException ex){
			c = loadCertificateDER(fileName);
		}
		return c;
	}

}
